package com.ejlchina.tool.network


import android.content.Context
import com.adolf.androiddemo.R
import com.ejlchina.data.Mapper
import com.ejlchina.okhttps.HTTP
import com.ejlchina.okhttps.HttpResult
import com.ejlchina.okhttps.gson.GsonMsgConvertor
import com.ejlchina.okhttps.okhttp.OkHttpClientWrapper
import com.ejlchina.tool.constant.BASE_URL_PROD
import com.ejlchina.tool.constant.DEFAULT_DOMAIN
import com.ejlchina.tool.constant.SP_DOMAIN
import com.ejlchina.tool.singleton.SharedPreferenceUtil
import okhttp3.logging.HttpLoggingInterceptor
import java.io.IOException
import java.net.SocketTimeoutException

class NetHttpManager {
    lateinit var myHttp: HTTP
    var context: Context? = null
    var exceptionCount: Int = 0

    companion object {
        fun getInstance() = InstanceHelper.sSingle
        val tagAuth = "Auth"
    }

    object InstanceHelper {
        val sSingle = NetHttpManager()
    }

    fun initCtx(ctx: Context) {
        context = ctx
        myHttp = HTTP.builder()
            .addMsgConvertor(GsonMsgConvertor())
            .baseUrl(BASE_URL_PROD + SharedPreferenceUtil.getString(SP_DOMAIN, DEFAULT_DOMAIN))
            .config {
                it.addInterceptor(HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY))
            }
            .addSerialPreprocessor { itPreChain ->
                if (!itPreChain.task.isTagged(tagAuth)) {
                    itPreChain.proceed()
                    return@addSerialPreprocessor
                }
                requestTokenAndRefreshIfExpired {
                    itPreChain.task.addHeader("Access-Token", it)
                    itPreChain.proceed()
                }
            }
            .build()
    }

    /**
     * 获取TOKEN，若过期则刷新（代码中的字符串可以替换为常量）
     */
    fun requestTokenAndRefreshIfExpired(callback: (String?) -> Unit) {
        val token = Token.fromSp(context!!)
        if (token == null) {
            doLogin(callback)
            return
        }
        if (token.isValid()) {
            callback(token.accessToken)
            return
        }
        // 访问令牌已过期，刷新令牌未过期，则调接口刷新当前令牌
        myHttp.async(TOKEN_REFRESH)
            .skipPreproc() // 跳过所有预处理器
            .addBodyPara("refreshToken", token.refreshToken)
            .nextOnIO()
            .setOnResponse { res: HttpResult ->
                exceptionCount = 0
                if (!res.isSuccessful) {
                    doLogin(callback)
                    return@setOnResponse
                }
                try {
                    val newToken = res.body.toBean(Token::class.java)
                    newToken.saveToSp(context!!)
                    callback(newToken.accessToken)
                } catch (e: Exception) {
                    doLogin(callback)
                    return@setOnResponse
                }
            }
            .setOnException { e: IOException ->
                evictHttpConnectPool(e)
                doLogin(callback)
            }
            .post()
    }

    private fun doLogin(callback: (String?) -> Unit) {
        myHttp.async(SIGN_IN)
            .skipPreproc()
            .addBodyPara("username", "IPC")
            .addBodyPara("password", "123456")
            .nextOnIO()
            .setOnResponse {
                exceptionCount = 0
                if (!it.isSuccessful) {
                    callback(null)
                    return@setOnResponse
                }
                try {
                    val newToken = it.body.toBean(Token::class.java)
                    newToken.saveToSp(context!!)
                    callback(newToken.accessToken)
                } catch (e: Exception) {
                    callback(null)
                    return@setOnResponse
                }
            }
            .setOnException {
                evictHttpConnectPool(it)
                callback(null)
            }
            .post()
    }

    fun doRequestNet(
        urlStr: String, isSkipPreproc: Boolean,
        bodyParas: Map<String, *>,
        callback: (HttpResult.Body?, String?, Int) -> Unit,
        isGet: Boolean, isAuth: Boolean,
        mapperCallBack: ((Mapper) -> Unit)? = null
    ) {
        var httpTask = myHttp.async(urlStr).setOnException {
            evictHttpConnectPool(it)
            callback(null, context?.getString(R.string.common_net_dis), 0)
        }
        if (isAuth) httpTask.tag(tagAuth)
        if (isSkipPreproc) {
            httpTask.skipPreproc()
        }
        httpTask.nextOnIO()
            .setOnResponse {
                exceptionCount = 0
                if (it.isSuccessful) {
                    callback(it.body, null, it.status)
                } else {
                    var bobyStr = it.body.toString()
                    callback(
                        null, if (bobyStr.isNullOrEmpty()) {
                            it.toString()
                        } else {
                            bobyStr
                        }, it.status
                    )
                }
            }
            .setOnException {
                evictHttpConnectPool(it)
                callback(null, context?.getString(R.string.common_net_dis), 0)
            }
        mapperCallBack?.let {
            httpTask.setOnResMapper {
                mapperCallBack.invoke(it)
            }
        }
//            .setOnResMapper {
//                mapperCallBack?.invoke(it)
//            }
        if (isGet) {
            httpTask.addUrlPara(bodyParas)
            httpTask.get()
        } else {
            httpTask.addBodyPara(bodyParas)
            httpTask.post()
        }
    }


    private fun evictHttpConnectPool(e: Exception) {
        try {
            if (e is SocketTimeoutException) {
                exceptionCount += 1
                if (exceptionCount >= 5) {
                    exceptionCount = 0
                    (myHttp as OkHttpClientWrapper).okClient().connectionPool.evictAll()
                }
            } else {
                exceptionCount = 0
            }
        } catch (e: Exception) {
        }
    }


}